﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Sintaxe;
using Sintaxe.Tokens;
using Sintaxe.Simbolos;
using Sintaxe.Instrucoes;


namespace XYSC1
{
    public class XYSC1_V2 : IProcessador
    {
        public readonly Tokens RegsGer;  // Registradores Gerais
        public readonly Tokens RegsEsp;  // Registradores Especiais
        public readonly Tokens _Mnemos;   // Instruções da maquina representados pelo código mnemónico
        public readonly Tokens Condicoes;

        public Tokens Mnemos()
        {
            return _Mnemos;
        }

        // constructor ====================
        //
        public XYSC1_V2()
        {
            RegsGer = IniRG();
            RegsEsp = IniRE();
            _Mnemos = IniMN();
            Condicoes = IniCC();
        }


        public byte[] Identificacao()
        {
            byte[] ID = new byte[4];
            ID[3] = (byte)'X';  
            ID[2] = (byte)'Y';  
            ID[1] = 1;// xysc1
            ID[0] = 2;// V2
            return ID;
        }

        public Type TipoInstrucao()
        {
            return typeof(IM_XYSC1_V2);
        }

        public UInt64 EndMax()
        {
            return (1L << 16) - 1;  // memória de 2^16 bytes
        }

        private Tokens IniRG()
        {
            // inicializar os registradores gerais
            Tokens tt = new Tokens();

            tt.Itens.Add(new TokenUint("r0", 0));
            tt.Itens.Add(new TokenUint("r1", 1));
            tt.Itens.Add(new TokenUint("r2", 2));
            tt.Itens.Add(new TokenUint("r3", 3));
            tt.Itens.Add(new TokenUint("r4", 4));
            tt.Itens.Add(new TokenUint("r5", 5));
            tt.Itens.Add(new TokenUint("r6", 6));
            tt.Itens.Add(new TokenUint("r7", 7));
            tt.Itens.Add(new TokenUint("r8", 8));
            tt.Itens.Add(new TokenUint("r9", 9));
            tt.Itens.Add(new TokenUint("rA", 10));
            tt.Itens.Add(new TokenUint("rB", 11));
            tt.Itens.Add(new TokenUint("rC", 12));
            tt.Itens.Add(new TokenUint("rD", 13));
            tt.Itens.Add(new TokenUint("rE", 14));
            tt.Itens.Add(new TokenUint("rF", 15));
            return tt;

        }
        private Tokens IniRE()
        {
            return null;
        }
        private Tokens IniMN()
        {
            Tokens tt = new Tokens();

            //tt.Itens.Add(new Mnemo("ADD", 0x31, MH_op_re_re, MV_mais4, FV_op_re_re));
            tt.Itens.Add(new Mnemo("ADD", 0x31, MH_op_re_re, MV_mais4, FV_op_re_re));
            tt.Itens.Add(new Mnemo("NOP", 0x61, MH_op, MV_mais4, FV_op));
            tt.Itens.Add(new Mnemo("JMP", 0x68, MH_op, MV_mais4, FV_op_im));

            return tt;
        }
        private Tokens IniCC()
        {
            return null;
        }


        // métodos para produzir representação binária
        void MH_op(ref Instrucao inst)
        {
            byte[] ba = new byte[4];
            uint OpCode = (_Mnemos.Item(inst.Operacao) as Mnemo).OpCode;
            ba[3] = Convert.ToByte(OpCode);
            inst.RepBin = ba;
        }


        void MH_op_re_re(ref Instrucao inst)
        {
            IM_XYSC1_V2 im = (inst as IM_XYSC1_V2);
            byte[] ba = new byte[4];

            uint OpCode = (_Mnemos.Item(im.Operacao) as Mnemo).OpCode;
            uint RD = (RegsGer.Item(im.RegDest) as TokenUint).Valor();
            uint RF = (RegsGer.Item(im.RegFont) as TokenUint).Valor();

            if (RD > 15) throw new ArgumentException("Número de registrador > 15 ilegítimo");
            if (RF > 15) throw new ArgumentException("Número de registrador > 15 ilegítimo");

            ba[3] = Convert.ToByte(OpCode);
            ba[2]  = Convert.ToByte(RD);
            ba[2] *= 16;  // 4 bits à esquerda
            ba[2] += Convert.ToByte(RF);

            inst.RepBin = ba;
        }

        // métodos para derivar o próximo endereço
        //
        UInt64 MV_mais4(UInt64 Inicio, ref Instrucao Inst)
        {
            // Montar verticalmente para instruções. Tamanho = 4 bytes, alinhamento = *4

            // Alinhamento ao multiplo de 4. Pode causar lacunha no objeto binário
            UInt64 corr = Inicio % 4;
            if (corr != 0) Inst.edcAtual = Inicio + (4 - corr);
            else Inst.edcAtual = Inicio;

            // Determinar próximo endereço livre na memória
            Inst.edcProx = Inst.edcAtual + 4;
            return  Inst.edcProx;
        }


        // Formatos para representação visual
        //
        string FV_op = "{0} {1} {6}";
        string FV_op_im = "{0} {1} {5} {6}";
        string FV_op_re_re = "{0} {1} {2} {3} {6}";
    }
}
